package cn.hg.solon.youcan.system.provider;

import cn.hg.solon.youcan.common.enums.BeanStatus;
import cn.hg.solon.youcan.common.exception.ServiceException;
import cn.hg.solon.youcan.easyquery.util.EntityQueryableUtil;
import cn.hg.solon.youcan.system.entity.*;
import cn.hg.solon.youcan.system.entity.proxy.EqPermissionProxy;
import cn.hg.solon.youcan.system.service.PermissionService;
import com.easy.query.api.proxy.client.EasyEntityQuery;
import com.easy.query.api.proxy.entity.select.EntityQueryable;
import com.easy.query.core.basic.api.select.Query;
import com.easy.query.solon.annotation.Db;
import org.dromara.hutool.core.bean.BeanUtil;
import org.dromara.hutool.core.bean.copier.CopyOptions;
import org.dromara.hutool.core.collection.ListUtil;
import org.dromara.hutool.core.math.NumberUtil;
import org.dromara.hutool.core.text.StrValidator;
import org.dromara.hutool.core.tree.MapTree;
import org.dromara.hutool.core.tree.TreeNodeConfig;
import org.dromara.hutool.core.tree.TreeUtil;
import org.dromara.hutool.core.util.ObjUtil;
import org.dromara.hutool.db.sql.Direction;
import org.dromara.hutool.db.sql.Order;
import org.noear.solon.annotation.Component;
import org.noear.solon.data.annotation.Tran;

import java.util.List;
import java.util.Map;

/**
 * @author 胡高
 */
@Component
public class EqPermissionProvider implements PermissionService {

    @Db("db1")
    private EasyEntityQuery easyEntityQuery;

    private EntityQueryable<EqPermissionProxy, EqPermission> buildQuery(Map<String, Object> paraMap) {
        String word = (String) paraMap.get("word");
        String type = (String) paraMap.get("type");
        String sortField = (String) paraMap.get("sortField");
        String sortType = (String) paraMap.get("sortType");

        // GROUP_CONCAT子查询（单列查询使用Query<T>做类型）
        Query<String> groupConcatQuery = this.easyEntityQuery
                // FROM sys_permission AS t1
                .queryable(EqPermission.class)
                // WHERE t1.`type` = ${type}
                .where(t1 -> {
                    t1.type().eq(StrValidator.isNotBlank(type), type);
                    // AND (t1.`name` LIKE '%${word}%' OR t1.`value` LIKE '%${word}%')
                    t1.or(StrValidator.isNotBlank(word), () -> {
                        t1.name().like(word);
                        t1.value().like(word);
                    });
                })
                // SELECT GROUP_CONCAT(t1.`ancestors`)
                .selectColumn(t1 -> t1.expression().sqlSegment("GROUP_CONCAT({0})",
                                c -> c.expression(t1.ancestors())
                        ).asAnyType(String.class)
                );

        EntityQueryable<EqPermissionProxy, EqPermission> entityQueryable = this.easyEntityQuery
                // FROM sys_permission AS t
                .queryable(EqPermission.class)
                .where(t -> t.or(() -> {
                    // WHERE (t.`type` = ${type} AND (t.`name` LIKE '%${word}%' OR t.`value` LIKE '%${word}%')
                    t.and(() -> {
                        t.type().eq(StrValidator.isNotBlank(type), type);
                        t.or(StrValidator.isNotBlank(word), () -> {
                            t.name().like(word);
                            t.value().like(word);
                        });
                    });
                    // OR FIND_IN_SET(t.`id`, ({GROUP_CONCAT子查询}))
                    t.expression().sql(StrValidator.isNotBlank(type) || StrValidator.isNotBlank(word), "FIND_IN_SET({0}, {1})", c -> {
                        c.expression(t.id());
                        c.expression(groupConcatQuery);
                    });
                }));

        // ORDER BY `sort`
        return EntityQueryableUtil.applyOrderBy(entityQueryable, new Order("sort", Direction.ASC));
    }

    private EntityQueryable<EqPermissionProxy, EqPermission> buildQueryByRole(Role role) {
        EntityQueryable<EqPermissionProxy, EqPermission> entityQueryable = this.easyEntityQuery
                // FROM sys_permission AS t
                .queryable(EqPermission.class)
                // LEFT JOIN sys_role_permission AS t1 ON t1.`permission_id` = t.`id`
                .leftJoin(EqRolePermissionMapping.class, (t, t1) -> t1.permissionId().eq(t.id()))
                // WHERE t.`status` = 'ON' AND t1.`role_id` = ${role.id}
                .where((t, t1) -> t1.roleId().eq(role.getId()))
                .distinct();

        // ORDER BY `sort`
        return EntityQueryableUtil.applyOrderBy(entityQueryable, new Order("sort", Direction.ASC));
    }

    private EntityQueryable<EqPermissionProxy, EqPermission> buildQueryByUser(User user) {
        EntityQueryable<EqPermissionProxy, EqPermission> entityQueryable = this.easyEntityQuery
                // FROM sys_permission AS t
                .queryable(EqPermission.class)
                // LEFT JOIN sys_role_permission AS t1 ON t1.`permission_id` = t.`id`
                .leftJoin(EqRolePermissionMapping.class, (t, t1) -> t1.permissionId().eq(t.id()))
                // LEFT JOIN sys_user_role AS t2 ON t2.`role_id` = t1.`role_id`
                .leftJoin(EqUserRoleMapping.class, (t, t1, t2) -> t2.roleId().eq(t1.roleId()))
                // WHERE t.`status` = 'ON' AND t2.`user_id` = ${user.id}
                .where((t, t1, t2) -> {
                    t.status().eq(BeanStatus.ON.name());
                    t2.userId().eq(user.getId());
                })
                .distinct();

        // ORDER BY `sort`
        return EntityQueryableUtil.applyOrderBy(entityQueryable, new Order("sort", Direction.ASC));
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#checkUnique(cn.hg.solon.youcan.system.entity.Permission)
     */
    @Override
    public boolean checkUnique(Permission bean) {
        return !this.easyEntityQuery
                // FROM sys_permission AS t
                .queryable(EqPermission.class)
                // WHERE t.`parent_id` = ${bean.parentId}
                .where(t -> {
                    t.parentId().eq(bean.getParentId());
                    // t.`name` = ${bean.name}
                    t.name().eq(bean.getName());
                    // t.`id` <> ${bean.id}
                    t.id().ne(ObjUtil.isNotNull(bean.getId()), bean.getId());
                })
                .any();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#delete(java.lang.Integer)
     */
    @Tran
    @Override
    public boolean delete(Integer id) {
        /*
         * 检查是否子项
         */
        if (this.easyEntityQuery.queryable(EqPermission.class).where(w -> w.parentId().eq(id)).any()) {
            throw new ServiceException("存在子项，不允许删除！");
        }

        /*
         * 检查是否已分配于角色
         */
        if (this.easyEntityQuery
                // FROM sys_permission AS t
                .queryable(EqPermission.class)
                // LEFT JOIN sys_role_permission_mapping AS t1 ON t1.`permission_id` = t.`id`
                .leftJoin(EqRolePermissionMapping.class, (t, t1) -> t1.permissionId().eq(t.id()))
                // LEFT JOIN sys_role AS t2 ON t2.`id` = t1.`role_id`
                .leftJoin(EqRole.class, (t, t1, t2) -> t2.id().eq(t1.roleId()))
                // WHERE t1.`permission_id` = ${id}
                .where((t, t1, t2) -> t1.permissionId().eq(id))
                .any()) {
            throw new ServiceException("已分配于角色，不允许删除！");
        }

        return this.easyEntityQuery.deletable(EqPermission.class).whereById(id).executeRows() > 0L;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#get(java.lang.Integer)
     */
    @Override
    public EqPermission get(Integer id) {
        return this.easyEntityQuery.queryable(EqPermission.class).whereById(id).firstOrNull();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#insert(cn.hg.solon.youcan.system.entity.Permission)
     */
    @Tran
    @Override
    public boolean insert(Permission bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }

        if (!this.checkUnique(bean)) {
            throw new ServiceException("权限名称已经存在，请更换其它值！");
        }

        EqPermission partner = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(partner) ? "0" : partner.getAncestors() + "," + bean.getParentId());

        EqPermission cloneBean = BeanUtil.copyProperties(bean, EqPermission.class);

        return this.easyEntityQuery.insertable(cloneBean).executeRows() > 0L;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#listBy(java.util.Map)
     */
    @Override
    public List<EqPermission> listBy(Map<String, Object> paraMap) {
        return this.buildQuery(paraMap).toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#listByRole(cn.hg.solon.youcan.system.entity.Role)
     */
    @Override
    public List<EqPermission> listByRole(Role bean) {
        if (ObjUtil.isNull(bean)) {
            return ListUtil.empty();
        }

        if (bean.getId() == 1) {
            return this.easyEntityQuery.queryable(EqPermission.class)
                    .where(t -> t.status().eq(BeanStatus.ON.name()))
                    .orderBy(t -> t.sort())
                    .toList();
        }

        return this.buildQueryByRole(bean).toList();
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#listByUser(cn.hg.solon.youcan.system.entity.User)
     */
    @Override
    public List<EqPermission> listByUser(User user) {
        if (ObjUtil.isNull(user)) {
            return ListUtil.empty();
        }

        if (user.getId() == 1) {
            return this.easyEntityQuery.queryable(EqPermission.class)
                    .where(w -> w.status().eq(BeanStatus.ON.name()))
                    .orderBy(t -> t.sort())
                    .toList();
        }

        return this.buildQueryByUser(user).toList();
    }

    @Override
    public List<MapTree<Integer>> treeBy(Map<String, Object> paraMap) {
        List<EqPermission> list = this.listBy(paraMap);

        TreeNodeConfig treeNodeConfig = new TreeNodeConfig();
        // 自定义属性名
        treeNodeConfig.setIdKey("id");
        treeNodeConfig.setParentIdKey("parentId");
        treeNodeConfig.setNameKey("name");

        List<MapTree<Integer>> trees = TreeUtil.build(list, 0, treeNodeConfig, (treeNode, tree) -> {
            tree.setId((Integer)treeNode.getId());
            tree.setParentId((Integer) treeNode.getParentId());
            tree.setName((String) treeNode.getName());
            //扩展属性
            tree.putExtra("disabled", !ObjUtil.equals(BeanStatus.ON.name(), treeNode.getStatus()));
        });

        return trees;
    }

    /* (non-Javadoc)
     * @see cn.hg.solon.youcan.system.service.PermissionService#update(cn.hg.solon.youcan.system.entity.Permission)
     */
    @Tran
    @Override
    public boolean update(Permission bean) {
        /*
         * 无父对象ID时，默认为0
         */
        if (ObjUtil.isNull(bean.getParentId())) {
            bean.setParentId(0);
        }
        Integer newParentId = bean.getParentId();

        if (NumberUtil.equals(newParentId, bean.getId())) {
            throw new ServiceException("上级不能为自己！");
        }

        if (!this.checkUnique(bean)) {
            throw new ServiceException("权限名称已经存在，请更换其它值！");
        }

        Permission parent = this.get(bean.getParentId());
        bean.setAncestors(ObjUtil.isNull(parent) ? "0" : parent.getAncestors() + "," + bean.getParentId());

        EqPermission cloneBean = this.get(bean.getId());
        Integer currentParentId = cloneBean.getParentId();

        BeanUtil.copyProperties(bean, cloneBean, CopyOptions.of().setIgnoreNullValue(true));

        boolean ret = this.easyEntityQuery.updatable(cloneBean).executeRows() > 0L;

        // 如果上级部门有变化，则更新所有下级部门的ancestors字段
        if (!ObjUtil.equals(newParentId, currentParentId)) {
            this.updateChildrenAncestors(cloneBean);
        }

        return ret;
    }

    private void updateChildrenAncestors(EqPermission bean) {
        // 查询所有直接下级
        List<EqPermission> childrenList = this.easyEntityQuery
                // FROM sys_dept AS t
                .queryable(EqPermission.class)
                // WHERE t.parent_id = {bean.id}
                .where(t -> t.parentId().eq(bean.getId()))
                .toList();

        // 更新下级的ancestors字段
        for (EqPermission item : childrenList) {
            item.setAncestors(bean.getAncestors() + "," + item.getParentId());
            this.easyEntityQuery.updatable(item).executeRows();

            // 递归更新再下一级
            this.updateChildrenAncestors(item);
        }
    }
}
